<!DOCTYPE html>
<html lang="en">
	<head>
		<title>Appearance Matching (Ward)</title>
		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
		<link href="css/bootstrap-2.1.1-spacelab.min.css" rel="stylesheet">
		<style type="text/css">
			canvas { pointer-events:none; z-index:10; }
			body {
				overflow	: hidden;
				padding		: 0;
				margin		: 0;
				color		: #222;
				background-color: #fff;
				font-family	: arial;
				font-size	: 100%;
			}
			#loading {
				color: #fff;
				background-color: rgba( 0, 0, 0, 0.5 );
				margin: 0px; padding: 10px;
				text-align:center;
				top: 0px; left: 0px;
				height: 100%;
				position: relative;
				z-index:1000;
			}
			#container {
				background-repeat: no-repeat;
				background-attachment: fixed;
				background-position: center;
				background-size: cover;
				background-color: #fff;
				padding: 0px; margin: 0px;
				overflow: hidden;
				text-align: center;
				top: 0px; left: 0px;
				width: 100%; height: 100%;
				position: absolute;
			}
		</style>
	</head>

	<body>
		<div id="loading">
			<h1>Loading...</h1>
			<div class="progress progress-striped active">
				<div class="bar" style="width: 100%;"></div>
			</div>
		</div>
		<div id="container"></div>
		<script type="text/javascript" src="js/jquery-1.8.2.min.js"></script>
		<script type="text/javascript" src="js/three.min.js"></script>
		<script type="text/javascript" src="js/Detector.js"></script>

<!-- Vertex shader -->
<script type="x-shader/x-vertex" id="vertexshader">
	// switch on high precision floats
	#ifdef GL_ES
	precision highp float;
	#endif

	varying vec3 normal_view; // world normal

	void main() {
		normal_view = normalMatrix * normal;
		vec3 vWorldPosition = (modelMatrix * vec4(position, 1.0)).xyz;
		gl_Position = projectionMatrix * (modelViewMatrix * vec4(position, 1.0));
	}
</script>

<!-- Fragment shader -->
<script type="x-shader/x-fragment" id="fragmentshader">
	#ifdef GL_ES
	precision highp float;
	#endif

	uniform float offx;
	uniform float offy;
	uniform sampler2D spheremap0;
	uniform sampler2D spheremap1;
	uniform vec3  albedo0;
	uniform vec3  albedo1;
	uniform float tonemap_A; // A = scale / (L_white^2), where scale = a / logavg(L_w)
	uniform float tonemap_B; // B = 1 / scale

	varying vec3 normal_view;

	vec3 decode(const in vec4 c) {
		return c.rgb * exp2(c.w * 255.0 - 128.0);
	}

	float luminance(vec3 c) {
		 return dot(vec3(0.2126, 0.7152, 0.0722), c);
	}

	float to_srgb(float value) {
		if (value < 0.0031308)
			return 12.92 * value;
		return 1.055 * pow(value, 1.0/2.4) - 0.055;
	}

	void main() {
		vec2 t1 = vec2(offx, offy) + 0.125 * normalize(normal_view).xy;
		vec2 t0 = vec2(0.5, 0.5) + 0.5 * normalize(normal_view).xy;

		vec3 c0 = albedo0 * decode(texture2D(spheremap0, t0));
		vec3 c1 = albedo1 * decode(texture2D(spheremap1, t1));
		vec3 c = c0 + c1;

		// Reinhard tonemapping with fixed constants
		float L_w = luminance(c);
		c *= (1.0 + tonemap_A * L_w) / (L_w + tonemap_B);

		gl_FragColor = vec4(to_srgb(c.r), to_srgb(c.g), to_srgb(c.b), 1.0);
	}
</script>

<!-- Render code -->
<script type="text/javascript">
	if ( ! Detector.webgl ) {
		Detector.addGetWebGLMessage();
		$('#loading').hide();
	}

	$(function() {
		// load renderer
		init();
		render();
	});

	var BSDF_TYPES = ["nonmetal", "metal"];
	var BSDF_NALPHAS = 16;

	// render params
	var container, loader;
	var camera, scene, renderer;
	var mesh, geometry;
	var scale = 1;
	var fov = 60;
	var spheremaps = []; // 0: nonmetal, 1: metal

	// rotation
	var isMouseDown = false, mouseX = 0, mouseY = 0;

	// params set by user UI
	var uiColor = 0x000000;  // hex color
	var uiType = 0; // [0, 2] int
	var uiAlpha = 0; // [0, 15] int
	var uiContrast = 0.0;
	var uiBrightness = 0.0;

	// global parameters
	var uiTonemapScale = 2.0;
	var uiTonemapWhite = 10.0;

	var loadingCount = 0;

	function getParameterByName(name)
	{
		name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
		var regexS = "[\\?&]" + name + "=([^&#]*)";
		var regex = new RegExp(regexS);
		var results = regex.exec(window.location.search);
		if(results == null)
			return "";
		else
			return decodeURIComponent(results[1].replace(/\+/g, " "));
	}

	function loadingStart() {
		$('#loading').show();
		loadingCount ++;
		if (parent !== undefined && parent.loading_start !== undefined) { parent.loading_start(); }
	}

	function loadingFinish() {
		loadingCount --;
		if (loadingCount == 0) { $('#loading').hide();
		}
		render();
		if (parent !== undefined && parent.loading_finish !== undefined) { parent.loading_finish(); }
	}

	function updatePhoto(img_url) {
		$('#container').css('background-image', "url('" + img_url + "')");
		console.log('Photo = ' + img_url);
	}

	function updateColor(hexStr) {
	    uiColor = parseInt(hexStr.substring(1, 7), 16);
		console.log('uiColor = ' + uiColor);
		render();
	}

	function updateContrast(f) {
		uiContrast = f;
		console.log('uiContrast = ' + uiContrast);
		render();
	}

	function updateAlpha(i) {
		uiAlpha = i;
		console.log('uiAlpha = ' + uiAlpha);
		render();
	}

	function updateType(t) {
		uiType = t;
		console.log('uiType = ' + uiType + ' (' + BSDF_TYPES[uiType] + ')');
		render();
	}

	function updateTonemapScale(t) {
		uiTonemapScale = t;
		console.log('uiTonemapScale = ' + uiTonemapScale);
		render();
	}

	function updateTonemapWhite(t) {
		uiTonemapWhite = t;
		console.log('uiTonemapWhite = ' + uiTonemapWhite);
		render();
	}

	function getScreenshot() {
		renderer.clear();
		renderer.render(scene, camera);
		return renderer.domElement.toDataURL();
	}

	function parseQueryParams() {
		var q_fov = getParameterByName('fov');
		if (q_fov) { fov = q_fov; }

		var q_color = getParameterByName('color');
		if (q_color) { uiColor = parseInt(q_color, 16); }

		var q_alpha = getParameterByName('alpha');
		if (q_alpha) { uiAlpha = Number(q_alpha); }

		var q_contrast = getParameterByName('contrast');
		if (q_contrast) { uiContrast = Number(q_contrast); }

		var q_type = getParameterByName('type');
		for (var i = 0; i < BSDF_TYPES.length; i ++) {
			if (q_type == BSDF_TYPES[i]) {
				uiType = i;
				break;
			}
		}
	}

	function init() {
		parseQueryParams();

		container = $('#container').get(0);
		scene = new THREE.Scene();

		camera = new THREE.PerspectiveCamera(fov,
				window.innerWidth / window.innerHeight, 1, 100);
		camera.position.z = 1.75 * scale /
			Math.tan(0.5 * fov * 3.14159265359 / 180);

		// setup material
		var blobbyMaterial = createMaterial();

		// init renderer
		renderer = new THREE.WebGLRenderer({ antialias: true, preserveDrawingBuffer: true});
		renderer.setSize(window.innerWidth, window.innerHeight);
		renderer.autoClear = false;
		container.appendChild(renderer.domElement);

		// start loading mesh
		loadingStart();
		loader = new THREE.BinaryLoader(true);
		loader.load("obj/blob.js", function(geometry) {
				geometryLoaded(geometry, blobbyMaterial); } );

		// init listeners
		window.addEventListener('resize', onWindowResize, false);
		container.addEventListener('mousedown', onDocumentMouseDown, false);
		container.addEventListener('mouseup', onDocumentMouseUp, false);
		container.addEventListener('mousemove', onDocumentMouseMove, false);

		// only consider loaded if webgl is okay
		if (Detector.webgl) {
			if (parent !== undefined && parent.loading_finish !== undefined) { parent.loading_finish(); }
		}
	}

	function createMaterial() {

		for (var i = 0; i < 2; i ++) {
			loadingStart();
			spheremaps[i] = THREE.ImageUtils.loadTexture('tex/ward-' + i + '-ennis.png', {}, loadingFinish);
			spheremaps[i].format = THREE.RGBAFormat;
			spheremaps[i].minFilter = THREE.NearestFilter;
			spheremaps[i].magFilter = THREE.NearestFilter;
			spheremaps[i].flipY = true;
		}

		var u = {
			"offx": { type: "f", value: 0.125 },
			"offy": { type: "f", value: 0.125 },
			"spheremap0": { type: "t", value: null },
			"spheremap1": { type: "t", value: null },
			"albedo0": { type: "c", value: new THREE.Color(0xffffff) },
			"albedo1": { type: "c", value: new THREE.Color(0xffffff) },
			"tonemap_A": { type: "f", value: 0.0 },
			"tonemap_B": { type: "f", value: 0.0 },
		};

		var vs = $('#vertexshader').text();
		var fs = $('#fragmentshader').text();

		var material = new THREE.ShaderMaterial( {
			vertexShader: vs,
			fragmentShader: fs,
			uniforms: u,
			depthWrite: true
		} );

		return material;
	}

	function onWindowResize() {
		camera.aspect = window.innerWidth / window.innerHeight;
		camera.updateProjectionMatrix();

		renderer.setSize(window.innerWidth, window.innerHeight);
		render();
	}

	function onDocumentMouseDown(event) {
		isMouseDown = true;
		mouseX = event.clientX;
		mouseY = event.clientY;
	}

	function onDocumentMouseUp(event) {
		isMouseDown = false;
	}

	function onDocumentMouseMove(event) {
		if (isMouseDown && mesh) {
			mesh.rotation.y += 0.015 * (event.clientX - mouseX);
			mesh.rotation.x += 0.015 * (event.clientY - mouseY);
			mouseX = event.clientX;
			mouseY = event.clientY;
			render();
		}
	}

	function geometryLoaded(geometry, m1) {
		mesh = new THREE.Mesh(geometry, m1);
		mesh.scale.x = mesh.scale.y = mesh.scale.z = scale;
		scene.add(mesh);
		loadingFinish();
	}

	function animate() {
		render();
		requestAnimationFrame(animate);
	}

	//function cielab_inv_f(t) {
	//	return t * t * t;
	//	//console.log('t: ' + t);
	//	//var f = 6.0 / 29.0;
	//	//if (t > f) {
	//	//	return t * t * t;
	//	//} else {
	//	//	console.log('f: ' + f);
	//	//	return 3 * f * f * (t - 4.0 / 29.0);
	//	//}
	//}

	function render() {
		if (!mesh) { return; }
		var u = mesh.material.uniforms;
		u.spheremap0.value = spheremaps[0];
		u.spheremap1.value = spheremaps[1];

		var row = Math.floor(uiAlpha / 4);
		var col = uiAlpha % 4;
		u.offx.value = (col + 0.5) / 4.0;
		u.offy.value = 1.0 - (row + 0.5) / 4.0;

		var color = new THREE.Color();
		color.setHex(uiColor);

		var B = Math.max(color.r, color.g, color.b);
		if (B > 0) {
			color.r /= B;
			color.g /= B;
			color.b /= B;
		}
		var finv = B * B * B;//cielab_inv_f(B);
		console.log ('finv: ' + finv);

		if (uiType == 0) { // non-metallic
			var rho_d = finv;
			var t = uiContrast + Math.pow(rho_d * 0.5, 1.0 / 3);
			var rho_s = t * t * t - rho_d * 0.5;

			if (rho_s + rho_d > 1) {
				var total = rho_s + rho_d;
				rho_s /= total;
				rho_d /= total;
			}

			u.albedo0.value.r = rho_d * color.r;
			u.albedo0.value.g = rho_d * color.g;
			u.albedo0.value.b = rho_d * color.b;
			u.albedo1.value.r = rho_s;
			u.albedo1.value.g = rho_s;
			u.albedo1.value.b = rho_s;
		} else { // metallic
			var rho_s = finv;

			u.albedo0.value.r = 0;
			u.albedo0.value.g = 0;
			u.albedo0.value.b = 0;

			u.albedo1.value.r = rho_s * color.r;
			u.albedo1.value.g = rho_s * color.g;
			u.albedo1.value.b = rho_s * color.b;
		}

		console.log ('rho_d: ' + rho_d);
		console.log ('rho_s: ' + rho_s);

		u.tonemap_A.value = uiTonemapScale / (uiTonemapWhite * uiTonemapWhite);
		u.tonemap_B.value = 1.0 / Math.max(1e-6, uiTonemapScale);

		renderer.clear();
		renderer.render(scene, camera);
	}

</script>
</body>
</html>
